////////////////////////////////////////////////////////////////////////////////
// Filename: shadow.fx
////////////////////////////////////////////////////////////////////////////////

#define NUM_LIGHTS 4
//////////////
// MATRICES //
//////////////
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;

matrix lightViewMatrix;
matrix lightProjectionMatrix;
matrix lightViewMatrix2;
matrix lightProjectionMatrix2;
matrix lightViewMatrix3;
matrix lightProjectionMatrix3;
matrix lightViewMatrix4;
matrix lightProjectionMatrix4;

//////////////
// TEXTURES //
//////////////
Texture2D shaderTexture;
Texture2D depthMapTexture;
Texture2D depthMapTexture2;
Texture2D depthMapTexture3;
Texture2D depthMapTexture4;

/////////////
// GLOBALS //
/////////////
float4 ambientColor;
float passnumber;
float4 diffuseColor;
float3 lightPosition;
float4 diffuseColor2;
float3 lightPosition2;
float4 diffuseColor3;
float3 lightPosition3;
float4 diffuseColor4;
float3 lightPosition4;


///////////////////
// SAMPLE STATES //
///////////////////
SamplerState SampleTypeClamp
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Clamp;
    AddressV = Clamp;
};

SamplerState SampleTypeWrap
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};


//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
};

struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
    float4 lightViewPosition : TEXCOORD1;
    float3 lightPos : TEXCOORD2;
    float4 lightViewPosition2 : TEXCOORD3;
    float3 lightPos2 : TEXCOORD4;
	float4 lightViewPosition3 : TEXCOORD5;
    float3 lightPos3 : TEXCOORD6;
	float4 lightViewPosition4 : TEXCOORD7;
    float3 lightPos4 : TEXCOORD8;
};


////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType ShadowVertexShader(VertexInputType input)
{
    PixelInputType output;
    float4 worldPosition;
    
    
    // Change the position vector to be 4 units for proper matrix calculations.
    input.position.w = 1.0f;

    // Calculate the position of the vertex against the world, view, and projection matrices.
    output.position = mul(input.position, worldMatrix);
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);
    
    // Calculate the position of the vertice as viewed by the light source.
    output.lightViewPosition = mul(input.position, worldMatrix);
    output.lightViewPosition = mul(output.lightViewPosition, lightViewMatrix);
    output.lightViewPosition = mul(output.lightViewPosition, lightProjectionMatrix);
    // Calculate the position of the vertice as viewed by the second light source.
    output.lightViewPosition2 = mul(input.position, worldMatrix);
    output.lightViewPosition2 = mul(output.lightViewPosition2, lightViewMatrix2);
    output.lightViewPosition2 = mul(output.lightViewPosition2, lightProjectionMatrix2);
	// Calculate the position of the vertice as viewed by the third light source.
    output.lightViewPosition3 = mul(input.position, worldMatrix);
    output.lightViewPosition3 = mul(output.lightViewPosition3, lightViewMatrix3);
    output.lightViewPosition3 = mul(output.lightViewPosition3, lightProjectionMatrix3);
	// Calculate the position of the vertice as viewed by the fourth light source.
    output.lightViewPosition4 = mul(input.position, worldMatrix);
    output.lightViewPosition4 = mul(output.lightViewPosition4, lightViewMatrix4);
    output.lightViewPosition4 = mul(output.lightViewPosition4, lightProjectionMatrix4);
    // Store the texture coordinates for the pixel shader.
    output.tex = input.tex;
    
    // Calculate the normal vector against the world matrix only.
    output.normal = mul(input.normal, (float3x3)worldMatrix);
	
    // Normalize the normal vector.
    output.normal = normalize(output.normal);

    // Calculate the position of the vertex in the world.
    worldPosition = mul(input.position, worldMatrix);

    // Determine the light position based on the position of the light and the position of the vertex in the world.
    output.lightPos = lightPosition.xyz - worldPosition.xyz;

    // Normalize the light position vector.
    output.lightPos = normalize(output.lightPos);

    // Determine the second light position based on the position of the light and the position of the vertex in the world.
    output.lightPos2 = lightPosition2.xyz - worldPosition.xyz;

    // Normalize the second light position vector.
    output.lightPos2 = normalize(output.lightPos2);

	// Determine the third light position based on the position of the light and the position of the vertex in the world.
    output.lightPos3 = lightPosition3.xyz - worldPosition.xyz;

    // Normalize the second light position vector.
    output.lightPos3 = normalize(output.lightPos3);

	// Determine the fourth light position based on the position of the light and the position of the vertex in the world.
    output.lightPos4 = lightPosition4.xyz - worldPosition.xyz;

    // Normalize the second light position vector.
    output.lightPos4 = normalize(output.lightPos4);

    return output;
}


////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 ShadowPixelShader(PixelInputType input) : SV_Target
{
    float bias;
    float4 color;
    float2 projectTexCoord;
    float depthValue;
    float lightDepthValue;
    float lightIntensity;
    float4 textureColor;


    // Set the bias value for fixing the floating point precision issues.
    bias = 0.001f;

    // Set the default output color to the ambient light value for all pixels.
    color = ambientColor;

	//Calculate the color/shadow for the first light as normal except for that we don't saturate a final light color anymore.

    // Calculate the projected texture coordinates.
    projectTexCoord.x =  input.lightViewPosition.x / input.lightViewPosition.w / 2.0f + 0.5f;
    projectTexCoord.y = -input.lightViewPosition.y / input.lightViewPosition.w / 2.0f + 0.5f;

    // Determine if the projected coordinates are in the 0 to 1 range.  If so then this pixel is in the view of the light.
    if((saturate(projectTexCoord.x) == projectTexCoord.x) && (saturate(projectTexCoord.y) == projectTexCoord.y))
    {
        // Sample the shadow map depth value from the depth texture using the sampler at the projected texture coordinate location.
        depthValue = depthMapTexture.Sample(SampleTypeClamp, projectTexCoord).r;

        // Calculate the depth of the light.
        lightDepthValue = input.lightViewPosition.z / input.lightViewPosition.w;

        // Subtract the bias from the lightDepthValue.
        lightDepthValue = lightDepthValue - bias;

        // Compare the depth of the shadow map value and the depth of the light to determine whether to shadow or to light this pixel.
        // If the light is in front of the object then light the pixel, if not then shadow this pixel since an object (occluder) is casting a shadow on it.
        if(lightDepthValue < depthValue)
        {
            // Calculate the amount of light on this pixel.
            lightIntensity = saturate(dot(input.normal, input.lightPos));
            if(lightIntensity > 0.0f)
            {
                // Determine the final diffuse color based on the diffuse color and the amount of light intensity.
                color += (diffuseColor * lightIntensity);
            }
        }
    }

	//Now do the same thing for the second light using the second light's shadow map and light variables. And instead of having a separate color result we just add the value to the color result from the first light.

    // Second light.
    projectTexCoord.x =  input.lightViewPosition2.x / input.lightViewPosition2.w / 2.0f + 0.5f;
    projectTexCoord.y = -input.lightViewPosition2.y / input.lightViewPosition2.w / 2.0f + 0.5f;

    if((saturate(projectTexCoord.x) == projectTexCoord.x) && (saturate(projectTexCoord.y) == projectTexCoord.y))
    {
        depthValue = depthMapTexture2.Sample(SampleTypeClamp, projectTexCoord).r;

        lightDepthValue = input.lightViewPosition2.z / input.lightViewPosition2.w;
        lightDepthValue = lightDepthValue - bias;

        if(lightDepthValue < depthValue)
        {
            lightIntensity = saturate(dot(input.normal, input.lightPos2));
            if(lightIntensity > 0.0f)
            {
                color += (diffuseColor2 * lightIntensity);
            }
        }
    }
	// Third light.
    projectTexCoord.x =  input.lightViewPosition3.x / input.lightViewPosition3.w / 2.0f + 0.5f;
    projectTexCoord.y = -input.lightViewPosition3.y / input.lightViewPosition3.w / 2.0f + 0.5f;

    if((saturate(projectTexCoord.x) == projectTexCoord.x) && (saturate(projectTexCoord.y) == projectTexCoord.y))
    {
        depthValue = depthMapTexture3.Sample(SampleTypeClamp, projectTexCoord).r;

        lightDepthValue = input.lightViewPosition3.z / input.lightViewPosition3.w;
        lightDepthValue = lightDepthValue - bias;

        if(lightDepthValue < depthValue)
        {
            lightIntensity = saturate(dot(input.normal, input.lightPos3));
            if(lightIntensity > 0.0f)
            {
                color += (diffuseColor3 * lightIntensity);
            }
        }
    }
	// Fourth light.
    projectTexCoord.x =  input.lightViewPosition4.x / input.lightViewPosition4.w / 2.0f + 0.5f;
    projectTexCoord.y = -input.lightViewPosition4.y / input.lightViewPosition4.w / 2.0f + 0.5f;

    if((saturate(projectTexCoord.x) == projectTexCoord.x) && (saturate(projectTexCoord.y) == projectTexCoord.y))
    {
        depthValue = depthMapTexture4.Sample(SampleTypeClamp, projectTexCoord).r;

        lightDepthValue = input.lightViewPosition4.z / input.lightViewPosition4.w;
        lightDepthValue = lightDepthValue - bias;

        if(lightDepthValue < depthValue)
        {
            lightIntensity = saturate(dot(input.normal, input.lightPos4));
            if(lightIntensity > 0.0f)
            {
                color += (diffuseColor4 * lightIntensity);
            }
        }
    }

    // Saturate the final light color.
    color = saturate(color);

    // Sample the pixel color from the texture using the sampler at this texture coordinate location.
    textureColor = shaderTexture.Sample(SampleTypeWrap, input.tex);

    // Combine the light and texture color.
    color = color * textureColor;

    return color;
}
float4 ShadowPixelShaderAmbient(PixelInputType input) : SV_Target
{
    float4 color;
    float4 textureColor;

    // Saturate the final light color.
    color = saturate(ambientColor);

    // Sample the pixel color from the texture using the sampler at this texture coordinate location.
    textureColor = shaderTexture.Sample(SampleTypeWrap, input.tex);

    // Combine the light and texture color.
    color = color * textureColor;

    return color;
}
//blend states
BlendState NoBlending
{
BlendEnable[0] = FALSE;
};
BlendState AdditiveBlending
{
AlphaToCoverageEnable = FALSE;
BlendEnable[0] = TRUE;
SrcBlend = ONE;
DestBlend = ONE;
BlendOp = ADD;
SrcBlendAlpha = ZERO;
DestBlendAlpha = ZERO;
BlendOpAlpha = ADD;
RenderTargetWriteMask[0] = 0x0F;
};
//depthstates
DepthStencilState EnableDepth
{
DepthEnable = TRUE;
DepthWriteMask = ALL;
};
//cullign
RasterizerState DisableCulling
{
CullMode = NONE;
};

RasterizerState EnableCulling
{
CullMode = BACK;
};
////////////////////////////////////////////////////////////////////////////////
// Technique
////////////////////////////////////////////////////////////////////////////////
technique10 ShadowTechnique
{
    pass pass0
    {
        SetVertexShader(CompileShader(vs_4_0, ShadowVertexShader()));
        SetPixelShader(CompileShader(ps_4_0, ShadowPixelShader()));
        SetGeometryShader(NULL);
		//SetBlendState( AdditiveBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
		//SetDepthStencilState( EnableDepth, 0 );
		//SetRasterizerState( EnableCulling );
    }
}
technique10 AmbientShadowTechnique
{
    pass pass0
    {
        SetVertexShader(CompileShader(vs_4_0, ShadowVertexShader()));
        SetPixelShader(CompileShader(ps_4_0, ShadowPixelShaderAmbient()));
        SetGeometryShader(NULL);
		SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
		SetDepthStencilState( EnableDepth, 0 );
		SetRasterizerState( EnableCulling );
    }
}